-
Notifications
You must be signed in to change notification settings - Fork 179
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add RPC API endpoint for tumbler #1252
Conversation
Thanks very much for this (at least potentially!) substantial contribution :) I won't be reviewing this over the next few days (not stopping anyone else ofc!) but it will definitely be a priority shortly. The general idea looks right; and to the extent that there are going to be any problems with this, I suspect it's more to do with underlying issues in the tumbler code/processing (I'm particularly thinking of recent issues that have been seen with the There is another more general architectural/design/philosophy discussion too: where should the 'business logic' be (to the extent that that phrase makes sense, it has to start in Whether a web client calling over the RPC-API should be doing any or all of that "business logic layer" is debatable. For now this is for sure the natural starting point. I just hope we can make it work! |
Thanks @AdamISZ! Take your time and just let us know once you have feedback. Some background on our reasoning for where to put the "business logic" part: I personally agree that ideally in future iterations we'd move towards an RPC API interface that just accepts a schedule and executes it "mindlessly". We've kept the "generating the schedule/business logic" part on the server side (for now!), mostly to avoid dragging the current schedule syntax too much into the public interface of the RPC API since there were some discussions around it in the past. For now we just tried to replicate the interface of the Anyway, looking forward to hearing your thoughts! I'm open for any suggestions. :) |
I have now tested the PR quite a bit and must say: Works great - no major problems. One thing I noticed was that the entries in the schedule log file are duplicated (not in jmwalletd log). All in all, I'd say: It works really good and there seems to be no situation a user can't recover from. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looks good to me, tested with Jam commit 39249a8.
I ran multiple schedules with different parameters on regtest, it worked every time. Granted, I always had only one counterparty, but that was only for easy testing. Shouldn't make a difference.
Example log output after success:
2022-04-28 11:48:20,731 Completed successfully the last entry:
2022-04-28 11:48:20,731 From mixdepth 3, sends amount: 25099949622 satoshis, without rounding, to destination address: bcrt1qfsmhpq7fvux98z79uf4e402fvuxrc0psdnpr37, after coinjoin with 1 counterparties.
In short: LGTM ✅
logsdir = os.path.join(os.path.dirname(jm_single().config_location), | ||
"logs") | ||
sfile = os.path.join(logsdir, self.tumbler_options['schedulefile']) | ||
with open(sfile, "wb") as f: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT: I wonder why this is "wb" instead of "w" since the schedule file is plain text? https://stackoverflow.com/a/7085623
(Edit: Yes, I meant "w", not "b" - corrected the comment.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean why is it "wb" instead of "w" right?
Good catch! I have to say I just blindly took what was already there in tumbler.py
. After testing with "w" it turns out that schedule_to_text
returns a bytes representation of the schedule, though. Therefore we need to write the file in bytes mode (even though the schedule format is in fact a plain text format). So in the end, I'd say keeping the bytes mode is the lesser evil than refactoring schedule_to_text
to return plain text (since it's used in a couple of places all over the app).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah just leave it for now, I guess it's an oversight because it is indeed a text format.
Forgot to mention: schedule "monitoring" works as expected too, whatever the |
Awesome, thanks for testing that as well! 🙏 |
|
||
# At the moment only the destination addresses can be set. | ||
# For now, all other options are hardcoded to the defaults of | ||
# the tumbler.py script. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a sensible simplification for a first step. Obviously in future we can POST
a json of all the options (admittedly there are many!)
@@ -424,14 +433,26 @@ def dummy_restart_callback(msg): | |||
token=self.cookie) | |||
|
|||
def taker_finished(self, res, fromtx=False, waittime=0.0, txdetails=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it would have been just as reasonable to make a tumbler_taker_finished
or similar, and set that as callback for that case (in start_tumbler
) but this way works well.
Of course it brings up that whole thing - the 'sendpayment vs tumbler' distinction is to my mind no longer the right distinction to make - there are only coinjoin schedules at the lower layer, with an offshoot being the direct_send
which is an entirely different animal. Your choice of endpoint name schedule
goes along with that.
I could argue this is perhaps an error; if in future we want the API to support any arbitrary schedule, then it would naturally fit under /schedule
, but this endpoint implicitly uses the tumbler options. Not sure if that matters right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree completely on the point about 'sendpayment vs tumbler'. I feel like mid-term this endpoint could evolve into simply taking any schedule via POST body and then executing it. For people who would want to use the default tumbler schedule, we could provide another endpoint GET /schedule
that takes the tumbler options as input and spits out a schedule (which can then subsequently be posted to POST /schedule
).
For now though I feel like the all-in-one function as it is now is probably the best (the easiest) way to start.
OK finished the first round. As you can see from the comments, the basic code structure you're using is definitely the right one as far as I'm concerned, at least to the extent we just want to get 'something working', so to speak. As the flavour of those comments probably communicate, I can see this being something that 'only just works', not because of anything bad in this PR at all, it's just that tumbler is very tricky. Even from the earliest days, this was the 'flagship product' of Joinmarket (so to speak, heh), but at the same time, by far the hardest thing to have work smoothly from start to finish (I vaguely remember Chris posting a poll on reddit, and a majority couldn't get it to work from start to finish without something going wrong .. a lot of what this repo started from was a desire to fix exactly that (see Another thing I didn't mention is I will test this code and see if it generally works - if it does I think we should merge it in next release but you must inform JAM users that is in a very minimal state (but you can also tell them it's not a disaster if it only half completes ... anyway it has to be looked at in that aspect). |
(Sorry here I made a mistake, I forgot that |
Able to complete a run of tumbler on regtest using curl against a running I'll note here the details of how I did that run. Before I start, it's important to make a fair comparison :) When I test tumbler as a script, or in JoinmarketQt on regtest, I always have to, similarly, use slightly artificial options settings, because of the limited environment that I create with
-N : (2,0) (only 3 makers available) As you can see, none of these changes are necessarily needed for a mainnet run. Though the flexibility is greatly desirable. There are several other settings where the default is absolutely fine, even for testing.
|
post: | ||
security: | ||
- bearerAuth: [] | ||
summary: create and run a schedule of transactions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per comment to the code, it's strange that we are describing and naming this after a procedure to pass in a schedule and run it, rather than what it actually currently is - pass in destinations (and perhaps other options in future), and let the code construct the schedule according to the tumbler algorithm.
tACK but with the obvious proviso (which is already implicit in the PR description, to be fair) that this is very bare bones and only really the most basic implementation and isn't guaranteed to always work (but I don't see it in any way as dangerous, hence the ACK part). |
I guess you mean |
Yes, I mean the It does not impair functionality and it seems a fix is in the making, so it should be fine 💪 For completeness, here is an example (after multiple start/stops on regtest):
|
Thanks a lot for the detailed review @AdamISZ -- It's very valuable feedback. I feel like we all agree that this is a MVP at best and that it'll need several iterations to become a robust and well usable for the average person. Until then, we'll take care to not oversell it in the UI and make sure the user is aware of that when using it.
ACK ✅ Regarding the tumbler options: I've been working on an update to this endpoint that allows passing all the tumbler options in addition to the destination addresses (with the default options as fallback). This is especially useful for testing without having to manually edit the tumbler code. This update also fixes the log issues that @theborakompanioni was seeing. Regarding the naming of the endpoint: I agree that it is slightly confusing. As noted above somewhere, in my opinion the endpoint could definitely evolve into something that takes any schedule and executes it in the future. Let us know if you think we should come up with another name for it until then or if this is fine for now. :) |
@theborakompanioni Oh, I see; I hadn't seen that behaviour at all before. Could you open an issue for it? |
Does anyone see a reason not to merge this now? The only thing I'm a bit uncertain about is the naming of the endpoint. That relates to something I simply neglected so far: the API "versioning" (called v1 for now) is not really functioning as versioning should, and I'm not sure how we should be doing it. When it comes to JM itself as software, we just leave it as |
I noticed, this issue is introduced by this PR. I fixed it in 1d5728f. The problem was that I was creating a new log handler for each time the API was called. It should be fixed now. |
I'm also unsure about that. One "hack" would be to keep the naming and in the future extend the endpoint in a backwards compatible way to either:
That would avoid any breaking changes and thus versioning issues. It's a hack though so I'm not sure if I like it. Open for suggestions. We can also rename the endpoint for now if that is deemed a better solution. :) |
No, it's fine. If the endpoint name |
Adds an API endpoint for the tumbler as laid out by @dergigi in #1239:
tumbler.py
script does)This can be seen as a minimal v1 of the tumbler API. Possible future updates would include: Restart support, fee estimation, support for custom schedules, etc.
We've discussed several options internally and also in #1239 and joinmarket-webui/jam#179 but ended up going with the most simple, minimally invasive approach for now.
We haven't added test (yet) but could definitely do so if needed once we agreed on the approach.
Let us know what you think and if you have any feedback on the approach taken. 🙏
Edit: We've tested the endpoint on regtest using the Jam Web UI and this branch. Anyone interested feel free to give it a spin.